using System;
using System.IO;

namespace AutoUpdateEngine.FileSystem
{
    /// <summary>
    /// a local file management tool
    /// </summary>
    public class LocalFileMgr
    {
        /// <summary>
        /// initial a local file management tool
        /// </summary>
        public LocalFileMgr()
        {
        }

        /// <summary>
        /// initial with the working directory specified
        /// </summary>
        /// <param name="rootDirect">it will be the default root </param>
        public LocalFileMgr(string rootDirect)
        {
            //this.rootDirect = rootDirect;
            RootDirectory = rootDirect;
            defaultRootDirect = RootDirectory;
        }

        /// <summary>
        /// the working directory of this manager
        /// </summary>
        public string RootDirectory
        {
            get { return rootDirect; }
            set
            {
                //ootDirect = value;
                SetRootDirect(value);
            }
        }

        /// <summary>
        /// set working directory back to the default root
        /// </summary>
        public void ResetRoot()
        {
            rootDirect = defaultRootDirect;
        }

        /// <summary>
        /// check if the subdirectory of the working directory exists
        /// </summary>
        /// <param name="directName">directName or (subdirectory\directName)</param>
        public bool SubDirectoryExists(string directName)
        {
            return Directory.Exists(AdjustPath(directName));
        }

        /// <summary>
        /// make a subdirectory under working directory
        /// </summary>
        /// <param name="directName">directName or (subdirectory\directName)</param>
        public void MakeSubDirectory(string directName)
        {
            if (SubDirectoryExists(directName))
                throw new Exception("can't create two subdirectories with the same name");

            Directory.CreateDirectory(AdjustPath(directName));
        }

        /// <summary>
        /// delete a subdirectory under working directory
        /// </summary>
        /// <param name="directName">directName or (subdirectory\directName)</param>
        public void DeleteSubDirectory(string directName)
        {
            if (SubDirectoryExists(directName))
                Directory.Delete(AdjustPath(directName), true);
        }

        /// <summary>
        /// check if the file under the working directory or subdirectories exists
        /// </summary>
        /// <param name="fileName">(filename) or (subdirectory\filename)</param>
        /// <returns></returns>
        public bool FileExists(string fileName)
        {
            return File.Exists(AdjustPath(fileName));
        }

        /// <summary>
        /// delete a file under the working directory or subdirectories
        /// </summary>
        /// <param name="fileName">(filename) or (subdirectory\filename)</param>
        public void DeleteFile(string fileName)
        {
            File.Delete(AdjustPath(fileName));
        }

        /// <summary>
        /// write the stream to a file with the name assigned on local disk
        /// </summary>
        /// <param name="readStream">(filename) or (subdirectory\filename)</param>
        /// <param name="fileName">(filename) or (subdirectory\filename)</param>
        public void CreateFile(Stream readStream, string fileName)
        {
            if (FileExists(fileName))
                throw new IOException("there is a file with this file name");

            byte[] buffer = new byte[BufferSize];
            Stream writeStream = File.Create(AdjustPath(fileName), buffer.Length);
            int bytesReaded = 0;
            do
            {
                bytesReaded = readStream.Read(buffer, 0, buffer.Length);
                writeStream.Write(buffer, 0, bytesReaded);
            } while (bytesReaded != 0);
            writeStream.Flush();

            readStream.Close();
            writeStream.Close();
        }

        /// <summary>
        /// rename a file 
        /// </summary>
        /// <param name="oldFileName">(filename) or (subdirectory\filename)</param>
        /// <param name="newFileName">(filename) or (subdirectory\filename)</param>
        public void Rename(string oldFileName, string newFileName)
        {
            if (FileExists(newFileName))
                throw new IOException("there is a file with the new file name");

            //File.Copy(AdjustPath(oldFileName), AdjustPath(newFileName), false);
            //DeleteFile(oldFileName);

            //Moving is faster than copy,especially for the large file, because moving just modify the reference rather than copying the whole content
            File.Move(AdjustPath(oldFileName), AdjustPath(newFileName));
        }

        /// <summary>
        /// get the absolute path from the given relative path
        /// </summary>
        /// <param name="subPath">an absolute or relative path</param>
        /// <returns>the absolute path </returns>
        public string AdjustPath(string subPath)
        {
            //if the subpath is a absolute path,return it directly
            //if(Path.IsPathRooted(subPath)) //don't work very well
            if (IsAbsolutePath(subPath))
                return subPath;

            //else transform it into a absolute path
            //if (!RootDirectory.EndsWith("\\"))
            //    RootDirectory = RootDirectory + "\\";
            if (subPath.StartsWith("\\"))
                subPath = subPath.Remove(0, 1);
            return RootDirectory + subPath;
        }

        private static bool IsAbsolutePath(string path)
        {
            if (string.IsNullOrEmpty(path))
                return false;

            path = path.Trim();
            if (path.IndexOf(":") == 1)
                return true;
            else
                return false;
        }

        /// <summary>
        /// append char '\' to the end of the root directory
        /// </summary>
        /// <param name="root"></param>
        private void SetRootDirect(string root)
        {
            if (!root.EndsWith("\\"))
                rootDirect = root + "\\";
            else
                rootDirect = root;
        }

        private string rootDirect, defaultRootDirect;
        private const int BufferSize = 1024;
    }
}